# -*- coding:utf-8 -*-

import re
import os
import time
from sys import path
from math import ceil
from hashlib import md5
import threading
import traceback
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QBrush, QColor, QPagedPaintDevice, QPdfWriter, QPainter, QFont, QFontMetrics, QFontInfo, QPalette, QPixmap
from PyQt5.QtCore import Qt, QIODevice, QFile, QMarginsF, QRect
from UI.Main_UI import Ui_MainWindow
from .myImageOCR import MyImageOCR
from .MyTableThread import MyTabThread
from .InfoWindows import InfoWindow


class MyMainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MyMainWindow, self).__init__(parent)
        self.setupUi(self)
        self.diyUi()  # 自己定义的一些样式
        self.initSlot()  # 初始化信号和槽
        # 以下为单图用到的变量
        ## 单图识别文件路径
        self.__file_name = ""
        ## OCR识别类型
        self.__signal_ocr_type = None
        ## OCR识别结果
        self.__ocr_words_result = list()
        ## OCR识别结果数
        ## 暂时没有用到
        self.__ocr_words_num = 0
        ## 关键词列表，应该被换成set
        self.__keywords_list = list()
        ## 读取关键字列表
        num = self.readKeyword()
        if num == -1:  # 读取关键词列表失败
            pass  # 如何终止运行？

        # 以下为第二页用到的变量
        ## 选择的文件夹路径
        self.__folder = ""
        ## 图片扩展名
        self.__img_filter_exts = list()
        ## 忽略小于此大小的文件（等于不忽略），单位KB
        self.__ignore_img_size = 0
        ## 保存待检测的图片的完整路径
        self.__img_lists = list()
        ## 所有图片的关键词搜索后的含有的关键词个数、等
        self.__keywords_search_res = dict()
        ## 是否检测完毕的标志
        self.__is_ocr_finish = False  # 是否进行过文字识别，且已经完成
        self.__child_window = None  # 用于显示详细信息的窗口，我觉得可以改成List

        # 线程标志
        self.__is_dfs_finish = True  # 是否有遍历图片的线程正在进行，且已经完成
        self.__ocr_thread_flag = 0  # 当前是否存在文字识别线程

    def initSlot(self):
        # 切换选项卡的信号
        # self.tabWidget.currentChanged.connect(self.changeSizeByTab)
        self.initUI_1()
        self.initUI_2()

    def diyUi(self):
        self.setShowResTableUi()

        # 使多图检测页的两个文字标签不可见
        self.search_status_label.setVisible(False)
        self.search_status_label_2.setVisible(False)

        # 设置背景
        palette = QPalette()
        palette.setBrush(QPalette.Background, QBrush(QPixmap("./PIC/background.jpg")))
        self.setPalette(palette)

        # qtabwidget半透明
        op = QGraphicsOpacityEffect()
        op.setOpacity(0.8)
        self.tabWidget.setGraphicsEffect(op)

        # Qtablewidget产生右键菜单
        self.show_res.setContextMenuPolicy(Qt.CustomContextMenu)  # 允许右键产生子菜单
        self.show_res.customContextMenuRequested.connect(self.generateMenu)  ####右键菜单

        # 禁止拉伸窗口大小
        self.setFixedSize(self.width(), self.height())

    def setShowResTableUi(self):
        # 设置列数
        self.show_res.setColumnCount(3)
        # 设置列标题，行号使用默认的
        self.show_res.setHorizontalHeaderLabels(["是否涉密", "文件名", "图片路径"])
        self.show_res.setColumnWidth(0, 81)
        self.show_res.setColumnWidth(1, 403)
        self.show_res.setColumnWidth(2, 405)
        # 禁止编辑表格
        self.show_res.setEditTriggers(QAbstractItemView.NoEditTriggers)
        # 将行的宽度设置与所显示的内容的宽度高度匹配
        # self.show_res.resizeColumnsToContents()
        self.show_res.resizeRowsToContents()
        # 设置表格头为伸缩模式
        # self.show_res.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        # 设置选中时是整行选中
        self.show_res.setSelectionBehavior(QAbstractItemView.SelectRows)
        # 根据内容自适应列宽
        # self.show_res.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
        # 列无滚动条
        # tw.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)

    # 添加数据到表格中
    def addTableItem(self, is_shemi, img_name, img_abs_path):
        row_c = self.show_res.rowCount()
        self.show_res.insertRow(row_c)
        new_item = QTableWidgetItem(is_shemi)
        self.show_res.setItem(row_c, 0, new_item)
        new_item = QTableWidgetItem(img_name)
        self.show_res.setItem(row_c, 1, new_item)
        new_item = QTableWidgetItem(img_abs_path)
        self.show_res.setItem(row_c, 2, new_item)
        # 添加完后将table的行数加一
        self.show_res.setRowCount(row_c + 1)
        # 调整列宽
        # self.show_res.resizeColumnsToContents()

    # 更新特定位置的数据
    def changeSpecialItem(self, row, col, new_str, color=None):
        print("执行修改表格函数")
        temp_item = QTableWidgetItem(new_str)
        if color == "red":
            temp_item.setForeground(QBrush(QColor(255, 0, 0)))
        self.show_res.setItem(row, col, temp_item)
        print("执行完毕")

    def readKeyword(self):  # try不能去掉，别的可以
        try:
            # 打开文档
            with open("keywords.txt", "r", encoding='utf-8') as foo:
                keywords = foo.read()
            self.__keywords_list.extend(keywords.split())  # 使用空格和换行进行分隔
            return 0
        except(Exception):
            traceback.print_exc()
            return -1

    # 自定义第二页的信号和槽（单图识别）
    def initUI_1(self):
        # 单图识别页的选择文件按钮
        self.pushButton_2.clicked.connect(self.chooseImageFile)
        # 开始识别按钮
        self.start_shibie_button.clicked.connect(self.startSignalOCR)
        # 保存识别结果
        self.save_file_button_1.clicked.connect(self.saveSignalOcrRes)

    # 自定义第二页的信号和槽（多图识别）
    def initUI_2(self):
        # 选择文件夹
        self.choose_folder_Button.clicked.connect(self.chooseFolder)
        # 开始寻找图片
        # self.start_search_img.clicked.connect(self.dfsFolder)
        self.start_search_img.clicked.connect(self.startDfsThread)
        # 开始OCR
        self.start_ocr_Button_2.clicked.connect(self.startOcrThread)
        # 测试
        self.pushButton_3.setVisible(True)
        self.pushButton_3.clicked.connect(self.generateReport)
        # 生成报告
        # self.baogao_button.clicked.connect(self.generateReport)

        pass

    def tests(self):
        try:
            print("测试开始")
            self.t = MyTabThread(self)
            print("测试结束")
            # self.changeSpecialItem(0, 0, "YES", "red")
        except(Exception):
            traceback.print_exc()

    # 选择图片文件
    def chooseImageFile(self):
        try:
            self.__file_name = QFileDialog.getOpenFileName(None, '选择一个图片`文件', './', 'Image(*.png *.jpg *.bmp *.jpeg)', 'Image(*.png *.jpg *.bmp *.jpeg)')[0]
            # 更新label的显示
            self.label.setText(self.__file_name or "未选择图像")
        except(Exception):
            traceback.print_exc()

    # 单图图像识别
    def startSignalOCR(self):
        try:
            if self.label.text() == "未选择文件":
                QMessageBox.information(QMessageBox(), "错误", "未选择图像", QMessageBox.Yes, QMessageBox.Yes)
                return
            self.label_3.setText("识别中...")
            # 读取识别方式
            current_index = self.comboBox.currentIndex()
            self.__signal_ocr_type = current_index
            self.__ocr_words_num = 0
            self.__ocr_words_result = list()
            # 根据识别方式进行识别
            if current_index == 0:  # 百度云
                temp_ocr_client = MyImageOCR(0)
                temp_num, temp_words_result = temp_ocr_client.startOcr(self.__file_name)
                if temp_num and temp_words_result:  # 两者非空
                    self.__ocr_words_num += temp_num  # 识别到的字符数
                    self.__ocr_words_result.extend(temp_words_result)  # 识别到的字符结果
            elif current_index == 1:  # 本地识别
                temp_ocr_client = MyImageOCR(1)
                temp_num, temp_words_result = temp_ocr_client.startOcr(self.__file_name)
                if temp_words_result:
                    self.__ocr_words_result.extend(temp_words_result)  # 识别到的字符结果
                else:
                    self.__ocr_words_result = list()
            # 显示识别结果
            self.displaySingleOcrRes()

        except(Exception):
            traceback.print_exc()

    '''
    function: 字符串与关键词进行匹配
    param: 待匹配的结果列表
    return: 返回识别结果列表，其中匹配到的关键字用<font color="res"></font>包围
    '''
    def searchKeywordsInOcrRes(self, ocr_res_words_list):
        # try可以删
        try:
            res_list = list()
            # 生成需被匹配的关键字字典
            # 参考链接：https://www.jb51.net/article/170153.htm
            keywords_replace_dict = dict()
            # print("in display: self.keywords_list = ")
            # print(self.__keywords_list)
            # print(type(self.__keywords_list))
            for i in self.__keywords_list:
                value = "<font color='red'>" + i + "</font>"
                keywords_replace_dict[i] = value
            # print("转换前的dict为：")
            # print(keywords_replace_dict)
            keywords_replace_dict = dict((re.escape(k), v) for k, v in keywords_replace_dict.items())
            # 编译正则表达式，提高速度
            pattern = re.compile("|".join(keywords_replace_dict.keys()))
            # 开始逐行匹配
            # for i in test_list:
            # print("self.__ocr_words_result = ")
            # print(type(self.__ocr_words_result))
            # print(self.__ocr_words_result)
            # for i in self.__ocr_words_result:
            for i in ocr_res_words_list:
                # print("i = ")
                # print(type(i))
                # print(i)
                res_list.append(pattern.sub(lambda m: keywords_replace_dict[re.escape(m.group(0))], i))
            return res_list
        except(Exception):
            traceback.print_exc()
            return None
        pass

    # 匹配并显示更改后的字符串
    def displaySingleOcrRes(self):
        # 关键词查找，oxr_words_list查完一行，显示一行
        # 当前时间复杂度：未知
        # 按理说 i 和 j 都是str
        try:
            display_text = ""  # 需要显示的最终结果
            count = 0  # 关键词个数
            self.textBrowser.setText("")
            # 如果没有识别结果
            if self.__ocr_words_num == 0 and self.__ocr_words_result is None:
                self.textBrowser.setText("您选择的图像中没有文字！！！")
            else:
                print("单词识别结果为：\n", self.__ocr_words_result)
                self.label_3.setText("关键词扫描中...")
                # 匹配完毕，显示字符串
                self.label_3.setText("识别结果如下（红字为违规关键词）：")
                temp_list = self.searchKeywordsInOcrRes(self.__ocr_words_result)
                for i in temp_list:
                    self.textBrowser.append(i)
        except(Exception):
            print("in display")
            traceback.print_exc()

    # 保存单图识别的结果，保存为txt文件
    def saveSignalOcrRes(self):
        if self.__ocr_words_result:
            t = QFileDialog.getSaveFileName(QFileDialog(), '另存为', './', '文本文件(*.txt)', '文本文件(*.txt)')[0]  # 获取保存的文件名
            print(t)
            # ('D:/学习档案/大三下/自己/保密督查-超星/课设/20174317-py/萨达萨达滴撒.txt', 'All Files (*)')
            # ('', '')
            if t:
                print("保存单个识别的结果-文件名存在")
                with open(t, "w") as foo:
                    for i in self.__ocr_words_result:
                        foo.write(i)
                        foo.write('\n')
        else:
            QMessageBox.information(QMessageBox(), "无法保存", "无文字信息", QMessageBox.Yes, QMessageBox.Yes)

    # 这里开始是第二页/多图的识别
    # 此函数用于读取并保存筛选条件
    def readImgFilter(self):
        # 读取扩展名
        if self.__img_filter_exts:
            self.__img_filter_exts.clear()
        if self.img_ext_1.isChecked():
            self.__img_filter_exts.append(".png")
        if self.img_ext_2.isChecked():
            self.__img_filter_exts.append(".bmp")
        if self.img_ext_3.isChecked():
            self.__img_filter_exts.append(".jpg")
            self.__img_filter_exts.append(".jpeg")

        print("after read img_exts, ext_list is ", self.__img_filter_exts)
        if len(self.__img_filter_exts) == 0:
            return False

        # 读取筛选大小
        size = self.size_spinBox.value()
        print("in size_spain_box = value type is ", size)
        # 读取筛选的尺寸类别
        size_type = self.size_type_comboBox.currentIndex()
        if size_type == 0:  # KB
            self.__ignore_img_size = size
        if size_type == 1:  # MB
            self.__ignore_img_size = size * 1024
        print("after read ignore_img_sie, it is ", self.__ignore_img_size)
        return True
        pass

    # 此函数用于判断一个文件是否是符合要求的图片文件
    def isAccordImg(self, img_path):
        # try可以删掉
        try:
            fname, fext = os.path.splitext(img_path)  # 如果图片没有扩展名怎么办？
            temp_ignore_size = self.__ignore_img_size*1024
            if fext:
                if fext.lower() in self.__img_filter_exts:  # 此文件的扩展名符合要求则继续
                    if os.path.getsize(img_path) >= temp_ignore_size:
                        return True
            return False
        except(Exception):
            traceback.print_exc()
            return False

    def chooseFolder(self):
        # try可以删掉
        try:
            if self.__folder:  # 清除上次选择的文件夹
                self.__folder = ""
            self.__folder += QFileDialog.getExistingDirectory(None, "选择需要识别的文件夹", "./", QFileDialog.ShowDirsOnly)
            if self.__folder:
                self.__folder = self.__folder.replace("/", "\\")
                self.folder_label.setText(self.__folder)
            else:
                self.folder_label.setText("未选择文件夹")
            print("self.__folder = ", self.__folder)
        except(Exception):
            traceback.print_exc()

    def dfsFolder(self):
        # try可以删除
        try:
            self.__is_ocr_finish = False
            # 修改标识位，避免出现线程冲突
            self.__is_dfs_finish = False

            if not self.__folder:  # 如果未选择文件夹
                QMessageBox.information(QMessageBox(), "错误", "未选择文件夹", QMessageBox.Yes, QMessageBox.Yes)
                self.__is_dfs_finish = True
                return

            # 修改label，显示查找状态
            self.search_status_label.setVisible(True)
            self.search_status_label.setText("查找中...")
            self.search_status_label_2.setVisible(True)
            self.search_status_label_2.setText("")

            # 读取并保存图片筛选条件
            if self.readImgFilter():  # 如果有筛选条件设置的不合理，会返回False
                # 首先清空上一次的查找结果
                if self.__img_lists:
                    self.__img_lists.clear()
                    self.kw_progressBar.setValue(0)  # 重置进度条
                    self.show_res.clearContents()  # 清空表格，不含表头
                    self.show_res.setRowCount(0)  # 清空表格，不含表头

                # 利用栈实现
                stack = list()  # 存放需要遍历的文件夹路径
                stack.append(self.__folder)
                s_count = 0
                while len(stack) > 0:
                    # 当栈不为空时，处理栈里的目录
                    current_folder = stack.pop()
                    # 获取目录中的文件目录
                    file_list = os.listdir(current_folder)
                    # 遍历
                    for i in file_list:
                        # 如果当前文件仍为目录
                        img_abs_path = os.path.join(current_folder, i)
                        if os.path.isdir(img_abs_path):
                            stack.append(img_abs_path)
                        # 不为目录则判断其是否符合筛选的条件
                        else:
                            s_count += 1
                            if self.isAccordImg(img_abs_path):
                                # 在列表中保存此图片
                                self.__img_lists.append(img_abs_path)
                                # 保存后显示到控件中
                                self.addTableItem("待检测", i, img_abs_path)
                    self.search_status_label_2.setText("已查找"+str(s_count)+",已找到"+str(len(self.__img_lists)))
                # 此处为dfs的测试
                print("After dfs, OK list is ")
                for i in self.__img_lists:
                    print(i)
                print("the count is ", len(self.__img_lists))
                self.search_status_label.setText("查找完成")
                pass
            else:
                QMessageBox.information(QMessageBox(), "错误", "筛选条件有误", QMessageBox.Yes, QMessageBox.Yes)
                self.__is_dfs_finish = True
                return
            # 查找完毕，设置标签
            self.__is_dfs_finish = True
        except(Exception):
            self.__is_dfs_finish = True
            traceback.print_exc()

    def startDfsThread(self):
        try:
            if not self.__is_dfs_finish:  # 检测是否已有遍历图片的任务
                QMessageBox.information(QMessageBox(), "错误", "您有图片查找任务未完成", QMessageBox.Yes, QMessageBox.Yes)
            elif self.__ocr_thread_flag == 1:  # 检测是否正在检测图片
                QMessageBox.information(QMessageBox(), "错误", "正在扫描图片", QMessageBox.Yes, QMessageBox.Yes)
            else:
                temp_thread = threading.Thread(target=self.dfsFolder)
                temp_thread.start()
        except(Exception):
            traceback.print_exc()

    def Partial_match_list(self, lst):  # 建立部分匹配表
        dp = [-1] + [0 for i in range(len(lst))]  # 留个-1，避免范围溢出
        r = 0  # 记录连续值
        for i in range(1, len(dp)):  # 跳过0
            if dp[i-1] and lst[i-1] == lst[r]:  # 判断1:如果上一个点存在值 #判断2：如果str当前值与记录值相同
                dp[i] = dp[i-1]+1  # 条件通过:当前dp值为上个dp值+1
                r = dp[i]  # 记录值与DP值相同
            else:  # 如果不符合上述条件
                dp[i] = 1 if lst[i-1] == lst[0] else 0  # 如果str当前值与首值相等，则当前dp为1，否则为0
                r = 1 if dp[i] else 0  # 如果当前dp为1，则记录值也为1,否则为0
        return dp[1:]  # 由于0号是建表初期拿来避免范围溢出的，后续用不到，所以不输出

    def KMP(self, text, mymatch):  # text为原文，match为匹配文本
        m, s = mymatch, text
        if len(m) > len(s):  # 如果查询字符大于文本，直接退出
            return -1
        dp = self.Partial_match_list(m)  # 使用先前代码制作PML
        index, dp_index = 0, 0  # 设定文本index与dp_index:文本index用于标注检测中的文本字符位置;dp_index用于标注检测中的匹配字符位置
        l, dp_l = len(s), len(dp)  # 计算文本长度与dp长度:文本长度用于检测循环是否到末尾;dp长度用于检测是否达成检索目标
        while index <= l - 1:  # 当文本index仍在合理值内，便继续循环
            if s[index - dp_index] == m[0]:  # s[index-dp_index]能准确指出当前匹配部分文本字符串的头号字符;如果其与匹配字符串的头号字符相等,则继续, 否则直接增加index去对照下一块文本
                while index <= l - 1 and s[index] == m[dp_index]:  # 当文本index仍在合理值内 且 当前文本字符与对应匹配字符相等，进入循环
                    if dp_index == dp_l - 1:  # 如果dp_index达到了最大理论长度，说明匹配字符全部通过匹配，程序完成
                        return index - dp_index  # 程序完成，进行反馈，返回发生匹配时最开始的序列
                    index += 1  # 如果还未匹配完毕,则增加index与dp_index，继续匹配
                    dp_index += 1
                else:
                    dp_part = dp[:dp_index]  # 当匹配出现不相等时，提取dp_part,其为目前已通过匹配的部分匹配字符串
                    dp_index = max(dp_part)  # 将dp_index重新定位到目前dp_part中的最大值，这是下一步要比较的dp_index
            else:
                index += 1  # 因为两组首字符不等，直接增加index比较下一组。
        return -1  # 原文中并没有出现匹配字符 返回-1

    '''
    关键词查找，查找其中是否有关键词，以及关键词个数
    res_list: 文字识别结果列表
    '''
    def Ocr_2(self, res_list):
        print("start Ocr_2")
        temp_text = "".join(res_list)

        count = 0  # 含有的关键词总数

        # KMP字符串匹配（需测试）
        keywords_res_dict = dict()
        for i in self.__keywords_list:
            temp_count = 0  # 当前关键词在字符串中的个数
            start = 0
            index_list = list()  # 若有关键字，则保存关键字所在的索引
            while True:
                print("start KMP")
                print("传入的文字为", temp_text[start:])
                temp_index = self.KMP(temp_text[start:], i)
                print("stop KMP, res = ", temp_index)
                if temp_index == -1:  # 未找到关键字
                    if len(index_list) != 0:
                        count += 1
                    break
                temp_count += 1
                index_list.append(temp_index)
                start += len(i)
            if temp_count > 0:
                keywords_res_dict[i] = temp_count  # 如果这个关键词有匹配到，加入到字典中
        print(keywords_res_dict)
        print("end Ocr_2")

        '''
        for i in self.__ocr_words_result:
            index_list = list()  # 一行一个index_list，索引记录
            keywords_res_dict = dict()  # 这一行的结果字典。字典大致是这样的：{"关键字": 数量, "下一个关键字": 数量, ...}
            for j in self.__keywords_list:
                temp_count = 0
                start = 0
                while True:
                    index = str(i).find(str(j), start)
                    if index == -1:
                        temp_count += 1
                        if len(index_list) != 0:
                            count += 1
                        break
                    index_list.append(index)
                    start = index + 1
            keywords_res_dict[j] = temp_count  # 如果这个关键词有匹配到，加入到字典中
        '''

        if count > 0:  # 含有关键词
            temp_list = self.searchKeywordsInOcrRes(res_list)
            return count, temp_list
        return 0, res_list

    def startOcrAndShow(self):
        try:
            print("start function startOcrAndShow")
            # 线程置标志位
            self.__ocr_thread_flag = 1

            temp_index = self.ocr_type_comboBox_2.currentIndex()  # 读取识别类型
            client = MyImageOCR(temp_index)  # 根据识别类型初始化不同的client
            print("img_list的总长度为：", len(self.__img_lists))

            # 创建子线程
            change_table_thread = MyTabThread()
            change_table_thread.start()  # 启动
            change_table_thread.mysingal.connect(self.changeSpecialItem)  # 绑定信号和槽

            # 开始识别
            for i in range(len(self.__img_lists)):
                res_num, res_list = client.startOcr(self.__img_lists[i])
                if res_num != 0:  # 如果有识别到文字
                    print("有文字")
                    num, kw_list = self.Ocr_2(res_list)  # 检查一下这里的逻辑
                    self.__keywords_search_res[self.__img_lists[i]] = {"count": num, "kw_list": kw_list}
                    print("已添加新的字典值，现在的字典为")
                    print(self.__keywords_search_res)
                    if num > 0:  # 如果其中含有关键词
                        print("有关键词")
                        change_table_thread.mysignalEmit(i, 0, "涉密", "red")
                        print("表格修改完成")
                    else:  # 没有关键词
                        print("无关键词")
                        # 修改对应行的“是否涉密”列
                        change_table_thread.mysignalEmit(i, 0, "未涉密", None)
                        print("表格修改完成")
                else:  # 无文字
                    print("无文字")
                    change_table_thread.mysignalEmit(i, 0, "无文字", None)
                    print("表格修改完成")
                # 更新进度条
                print("当前 i = ", i, "当前进度：", 100*((i+1)/len(self.__img_lists))//1)
                self.kw_progressBar.setValue(int(100*((i+1)/len(self.__img_lists))//1))
            self.__ocr_thread_flag = 0
            self.__is_ocr_finish = True
            change_table_thread.stop()  # 强制终止线程运行
            print("finish function startOcrAndShow")
        except(Exception):
            print("in except finish function startOcrAndShow")
            self.__ocr_thread_flag = 0
            traceback.print_exc()

    def startOcrThread(self):
        try:
            if self.__ocr_thread_flag == 1:  # 检测上一次扫描是否完成
                QMessageBox.information(QMessageBox(), "错误", "您有扫描任务正在执行", QMessageBox.Yes, QMessageBox.Yes)
                return
            if not self.__is_dfs_finish:  # 检测是否正在遍历图片
                QMessageBox.information(QMessageBox(), "错误", "正在查找图片", QMessageBox.Yes, QMessageBox.Yes)
                return
            if self.show_res.rowCount() == 0:  # 图片查找什么也没找到
                QMessageBox.information(QMessageBox(), "错误", "无图片需要识别", QMessageBox.Yes, QMessageBox.Yes)
                return
            temp_thread = threading.Thread(target=self.startOcrAndShow)
            temp_thread.start()
        except(Exception):
            traceback.print_exc()

    # 生成右键菜单
    def generateMenu(self, pos):
        row_num = -1
        print("pos=", pos)
        t = self.show_res.selectionModel().selection().indexes()
        print(t)

        # 如果这里有多行同时被选中呢？
        for i in self.show_res.selectionModel().selection().indexes():
            row_num = i.row()  # 这里有重复索引
            print("row_num= ", row_num)

        # 弹出菜单
        menu = QMenu()
        item1 = menu.addAction("转到文件所在位置")
        item2 = menu.addAction("详细信息")
        # 使菜单能在正常位置显示
        screen_pos = self.show_res.mapToGlobal(pos)

        # 单击一个菜单项就返回，使之被阻塞
        action = menu.exec_(screen_pos)
        if action == item1:
            temp_path = self.show_res.item(row_num, 2).text()
            os.system("explorer.exe /e,/select,"+temp_path)
        elif action == item2:
            try:
                print("生成新窗口")
                temp_name = self.show_res.item(row_num, 2).text()
                if not self.__keywords_search_res.get(temp_name):
                    QMessageBox.information(QMessageBox(), "错误", "无文字信息", QMessageBox.Yes, QMessageBox.Yes)
                    return
                # self.__child_window = InfoWindow(self.__keywords_search_res.get(temp_name).get("kw_list"))  # 参数，为识别列表
                self.__child_window = InfoWindow(self.__keywords_search_res.get(temp_name).get("kw_list"))  # 参数，为识别列表
                # self.__child_window.show()
                self.__child_window.show()
            except(Exception):
                traceback.print_exc()

    # 生成报告，使用QPdfwriter，未完成
    def generateReport(self):
        try:
            # return  # 需要更多的计算
            # 检测是否可以生成报告：检查是否检测完毕
            # self.__is_ocr_finish = True
            if self.__is_ocr_finish:
                base_path = os.path.join(path[0], "Report")  # 脚本所在目录
                # 检测文件夹是否存在
                if not os.path.exists(base_path):
                    os.mkdir(base_path)
                # 获取时间
                temp_time = time.strftime('%Y_%m_%d_%H_%M_%S', time.localtime(time.time()))
                filename = "检测报告_"+temp_time+".pdf"

                # 创建pdf
                pdfile = QFile(os.path.join(base_path, filename))
                pdfile.open(QIODevice.WriteOnly)
                # 创建pdf写入器
                pdf_writer = QPdfWriter(pdfile)
                # 设置纸张为A4
                pdf_writer.setPageSize(QPagedPaintDevice.A4)
                # 设置纸张分辨率（dpi）为300, 其像素为3424X2395
                print("default dpi is ", pdf_writer.resolution())
                dpi = 300
                pdf_writer.setResolution(dpi)
                print("new dpi is ", pdf_writer.resolution())

                # 边距，上3.6cm，下3cm，左右2.7cm
                # 英寸 = 像素/分辨率，一英寸 = 2.54厘米
                # pdf_writer.setPageMargins(QMarginsF(319, 425, 319, 354))  # left,top,right,buttom
                # print("before setPageMargins, pad_writer_wodth=", pdf_writer.width())
                # pdf_writer.setPageMargins(QMarginsF(27, 36, 27, 30))  # left,top,right,buttom 单位是毫米

                # 用于定位矩形的x，y坐标，单位是像素
                top_padding = 425
                bottom_padding = 345
                left_padding = 319
                right_padding = 319
                x_pos = 319  # 左边距
                y_pos = top_padding  # 上边距
                # 当前页剩余可用的区域高度
                remain_height = pdf_writer.height() - top_padding - bottom_padding  # 354是下边距对应的像素
                # 页面能写内容的宽度
                content_width = pdf_writer.width() - left_padding - right_padding

                pdf_painter = QPainter(pdf_writer)

                # 标题
                point_size = 22
                font = QFont()
                font.setFamily("SimSun.ttf")  # 宋体
                font.setPointSize(point_size)  # 二号字
                font.setBold(True)  # 加粗

                # 计算字体详细信息
                # 必须使用  pt*dpi/72 来计算， 不同的dpi下同一大小的字体所占的像素会有差异
                font_info = QFontMetrics(font)
                react_height = point_size*dpi/72  # 公式：pt*dpi/72
                separate = react_height + font_info.leading()

                # 让painter使用该字体
                pdf_painter.setFont(font)
                # 居中绘制标题
                pdf_painter.drawText(QRect(x_pos, y_pos, content_width, react_height), Qt.AlignHCenter, "检测报告")
                # y坐标下移
                y_pos += separate
                # 计算剩下的高度
                remain_height -= separate

                # 检测时间之类的，更换使用5号字
                point_size = 10
                font.setPointSize(point_size)
                font.setBold(False)
                pdf_painter.setFont(font)

                # print("default font metericts is ", font_info.ascent() + font_info.descent())
                react_height = point_size*dpi/72  # 更新
                # print("new react_height is", react_height)
                font.setPixelSize(react_height)
                font_info = QFontMetrics(font)

                '''
                print("")
                print("test")
                strrr = '一二三四五六七八九十'
                print("before set pixel, the width of '一二三四五六七八九十' is ", font_info.width(strrr))
                font.setPixelSize(react_height)
                font_info = QFontMetrics(font)
                print("after set pixel, the width of '一二三四五六七八九十' is ", font_info.width(strrr))
                '''

                separate = react_height + font_info.leading()  # 每写完一行后，y坐标需要偏移的量

                pdf_painter.drawText(QRect(x_pos, y_pos, content_width, react_height), Qt.AlignHCenter,
                                     "检测时间："+str(temp_time))
                y_pos += separate
                remain_height -= separate

                def writeAText(mystr, align=Qt.AlignLeft):
                    print("write a text")
                    nonlocal remain_height, y_pos
                    # 写完整个句子需要多少行
                    need_row = ceil(font_info.width(mystr) / content_width)
                    new_react_height = need_row * separate  # 计算将文本全部写到一个矩形里需要的高度

                    # 剩下的空间是否足够写完一行
                    if remain_height < new_react_height:  # 不够
                        # 建新页
                        pdf_writer.newPage()
                        # 重新设置坐标
                        y_pos = top_padding  # 上边距
                        # 当前页剩余可用的区域高度
                        remain_height = pdf_writer.height() - top_padding - bottom_padding

                    # 写入
                    # pdf_painter.drawRect(QRect(x_pos, y_pos, content_width, new_react_height))
                    pdf_painter.drawText(QRect(x_pos, y_pos, content_width, new_react_height),
                                         Qt.TextWordWrap | align,  # 对齐方式，自动换行
                                         mystr)

                    y_pos += new_react_height
                    remain_height -= new_react_height
                    
                print("")

                # 开始写入检测结果
                # 遍历qtablewidget
                for i in range(self.show_res.rowCount()):
                    print("begin")
                    if self.show_res.item(i, 2):
                        # 判断对应文件是否涉密
                        if self.show_res.item(i, 0).text() == "涉密":
                            # 生成需要写入的句子
                            # 1.先写入图片路径
                            temp_text = "图片路径：" + self.show_res.item(i, 2).text()
                            print("temp_text", i, " is ", temp_text)
                            print("temp_text is", font_info.width(temp_text))
                            # 计算写入这个句子需要多少行
                            writeAText(temp_text)

                            # 2.写入文件hash
                            temp_md5 = "MD5: " + md5(open(self.show_res.item(i, 2).text(), "rb").read()).hexdigest()
                            writeAText(temp_md5)
                            # 3.识别结果
                            text = self.__keywords_search_res.get(self.show_res.item(i, 2).text()).get("kw_list")
                            writeAText("识别结果：")
                            if text:
                                writeAText("".join(text))

                    else:
                        print(i, " is not exist")

                pdf_painter.end()
                pdfile.close()

                QMessageBox.information(QMessageBox(), "成功", "生成完毕，请到Report文件夹下查看报告", QMessageBox.Yes, QMessageBox.Yes)
            else:
                QMessageBox.information(QMessageBox(), "错误", "文字识别未完成", QMessageBox.Yes, QMessageBox.Yes)
        except(Exception):
            traceback.print_exc()
